{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import sys\n",
    "assert sys.version_info >= (3, 5)\n",
    "import numpy as np\n",
    "%matplotlib inline\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "mpl.rc('axes', labelsize=14)\n",
    "mpl.rc('xtick', labelsize=12)\n",
    "mpl.rc('ytick', labelsize=12)\n",
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "import pandas as pd\n",
    "from scipy import special\n",
    "from tensorflow.keras import layers\n",
    "np.random.seed(42)\n",
    "tf.random.set_seed(42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "M = 16\n",
    "k = int(np.log2(M))\n",
    "n = 1\n",
    "TRAINING_SNR = 7\n",
    "rayleigh = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def EbNo_to_noise(ebnodb):\n",
    "    '''Transform EbNo[dB]/snr to noise power'''\n",
    "    ebno = 10**(ebnodb/10)\n",
    "    noise_std = 1/np.sqrt(2*(k/n)*ebno) \n",
    "    return noise_std\n",
    "\n",
    "def SNR_to_noise(snrdb):\n",
    "    '''Transform EbNo[dB]/snr to noise power'''\n",
    "    snr = 10**(snrdb/10)\n",
    "    noise_std = 1/np.sqrt(2*snr)\n",
    "    return noise_std"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# From https://colab.research.google.com/github/google-research/google-research/blob/master/vbmi/vbmi_demo.ipynb#scrollTo=LwSIGeXlD11E\n",
    "# concatenated critic\n",
    "\n",
    "class NN_function(tf.keras.Model):\n",
    "    def __init__(self, hidden_dim, layers, activation, **extra_kwargs):\n",
    "        super(NN_function, self).__init__()\n",
    "        self._f = tf.keras.Sequential(\n",
    "          [tf.keras.layers.Dense(hidden_dim, activation) for _ in range(layers)] +\n",
    "          [tf.keras.layers.Dense(1)])\n",
    "\n",
    "    def call(self, x, y):\n",
    "        batch_size = tf.shape(x)[0]\n",
    "        x_tiled = tf.tile(x[None, :],  (batch_size, 1, 1))\n",
    "        y_tiled = tf.tile(y[:, None],  (1, batch_size, 1))\n",
    "        xy_pairs = tf.reshape(tf.concat((x_tiled, y_tiled), axis=2), [batch_size * batch_size, -1])\n",
    "        scores = self._f(xy_pairs)\n",
    "        return tf.transpose(tf.reshape(scores, [batch_size, batch_size]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "critic_params = {\n",
    "    'layers': 2,\n",
    "    'hidden_dim': 256,\n",
    "    'activation': 'relu',\n",
    "}\n",
    "\n",
    "def MINE(scores):  \n",
    "    def marg(x):\n",
    "        batch_size = x.shape[0]\n",
    "        marg_ = tf.reduce_mean(tf.exp(x - tf.linalg.tensor_diag(np.inf * tf.ones(batch_size))))\n",
    "        return marg_*((batch_size*batch_size)/(batch_size*(batch_size-1.)))\n",
    "    joint_term = tf.reduce_mean(tf.linalg.diag_part(scores))\n",
    "    marg_term = marg(scores)\n",
    "    return joint_term - tf.math.log(marg_term)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "noise_std = EbNo_to_noise(TRAINING_SNR)\n",
    "# custom functions / layers without weights\n",
    "norm_layer = keras.layers.Lambda(lambda x: tf.divide(x,tf.sqrt(2*tf.reduce_mean(tf.square(x)))))\n",
    "shape_layer = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2,n]))\n",
    "shape_layer2 = keras.layers.Lambda(lambda x: tf.reshape(x, shape=[-1,2*n]))\n",
    "channel_layer = keras.layers.Lambda(lambda x: \n",
    "                    x + tf.random.normal(tf.shape(x), mean=0.0, stddev=noise_std))\n",
    "def sample_Rayleigh_channel(x, noise_std):\n",
    "    h_sample = (1/np.sqrt(2))*tf.sqrt(tf.random.normal(tf.shape(x))**2+tf.random.normal(tf.shape(x))**2)\n",
    "    z_sample = tf.random.normal(tf.shape(x), stddev = noise_std)\n",
    "    y_sample = x + tf.divide(z_sample,h_sample)\n",
    "    return tf.cast(y_sample, tf.float32)\n",
    "\n",
    "rayleigh_channel_layer = keras.layers.Lambda(lambda x: sample_Rayleigh_channel(x,noise_std))\n",
    "\n",
    "\n",
    "encoder = keras.models.Sequential([\n",
    "            keras.layers.Embedding(M, M, embeddings_initializer='glorot_normal', input_length=1),\n",
    "            keras.layers.Dense(M, activation=\"elu\"),\n",
    "            keras.layers.Dense(2*n, activation=None),\n",
    "            shape_layer,\n",
    "            norm_layer])\n",
    "if rayleigh:\n",
    "    channel = keras.models.Sequential([rayleigh_channel_layer])\n",
    "else:\n",
    "    channel = keras.models.Sequential([channel_layer])\n",
    "\n",
    "decoder = keras.models.Sequential([\n",
    "                keras.layers.InputLayer(input_shape=[2,n]),\n",
    "                shape_layer2,\n",
    "                keras.layers.Dense(M, activation=\"elu\"),\n",
    "                keras.layers.Dense(M, activation=\"softmax\")\n",
    "                ])\n",
    "\n",
    "autoencoder = keras.models.Sequential([encoder, channel, decoder])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def B_Ber_m(input_msg, msg):\n",
    "    '''Calculate the Batch Bit Error Rate'''\n",
    "    batch_size = input_msg.shape[0]\n",
    "    pred_error = tf.not_equal(tf.reshape(input_msg, shape=(-1,batch_size)), tf.argmax(msg, 1)) \n",
    "    bber = tf.reduce_mean(tf.cast(pred_error, tf.float32))\n",
    "    return bber"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def random_sample(batch_size=32):\n",
    "    msg = np.random.randint(M, size=(batch_size,1))\n",
    "    return msg"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_encoding(M=16, n=1):\n",
    "    inp = np.arange(0,M)\n",
    "    coding = encoder.predict(inp)\n",
    "    fig = plt.figure(figsize=(4,4))\n",
    "    plt.plot(coding[:,0], coding[:, 1], \"b.\")\n",
    "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "    plt.gca().set_ylim(-2, 2)\n",
    "    plt.gca().set_xlim(-2, 2)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test_noisy_codeword(data):\n",
    "    rcvd_word = data[1:2000]\n",
    "    fig = plt.figure(figsize=(4,4))\n",
    "    plt.plot(rcvd_word[:,0], rcvd_word[:, 1], \"b.\")\n",
    "    plt.xlabel(\"$x_1$\", fontsize=18)\n",
    "    plt.ylabel(\"$x_2$\", fontsize=18, rotation=0)\n",
    "    plt.grid(True)\n",
    "    plt.gca().set_ylim(-2, 2)\n",
    "    plt.gca().set_xlim(-2, 2)\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_fn = keras.losses.SparseCategoricalCrossentropy()\n",
    "mean_loss = keras.metrics.Mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding):\n",
    "    template = 'Iteration: {}, Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
    "    if step % 10 == 0:\n",
    "        print(template.format(step, epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))\n",
    "        if plot_encoding:\n",
    "            test_encoding()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_batch_loss(epoch, mean_loss, X_batch, y_pred):\n",
    "        template_outer_loop = 'Interim result for Epoch: {}, Loss: {:.5f}, Batch_BER: {:.5f}'\n",
    "        print(template_outer_loop.format(epoch, mean_loss.result(), B_Ber_m(X_batch, y_pred)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_mi(NN_estimation, n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005):\n",
    "    optimizer_mi = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                x_enc = encoder(X_batch, training=True)\n",
    "                y_recv = channel(x_enc)\n",
    "                x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "                y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "                score = NN_estimation(x,y)\n",
    "                loss = -MINE(score)\n",
    "                gradients = tape.gradient(loss, NN_estimation.trainable_variables) \n",
    "                optimizer_mi.apply_gradients(zip(gradients, NN_estimation.trainable_variables))\n",
    "            mi_avg = -mean_loss(loss)\n",
    "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))\n",
    "        mean_loss.reset_states()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_decoder(n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.005, plot_encoding=True):\n",
    "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch  = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                y_pred = autoencoder(X_batch, training=True)\n",
    "                loss = tf.reduce_mean(loss_fn(X_batch, y_pred))\n",
    "                gradients = tape.gradient(loss, decoder.trainable_variables) \n",
    "                optimizer_ae.apply_gradients(zip(gradients, decoder.trainable_variables)) \n",
    "            mean_loss(loss)\n",
    "            plot_loss(step, epoch, mean_loss, X_batch, y_pred, plot_encoding)\n",
    "        plot_batch_loss(epoch, mean_loss, X_batch, y_pred) \n",
    "        mean_loss.reset_states()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_encoder(NN_estimation, n_epochs=5, n_steps=20, batch_size=200, learning_rate=0.05):\n",
    "    optimizer_mi = keras.optimizers.Nadam(lr=0.005)\n",
    "    optimizer_ae = keras.optimizers.Nadam(lr=learning_rate)\n",
    "    for epoch in range(1, n_epochs + 1): \n",
    "        print(\"Training Bob in Epoch {}/{}\".format(epoch, n_epochs)) \n",
    "        for step in range(1, n_steps + 1):\n",
    "            X_batch  = random_sample(batch_size)\n",
    "            with tf.GradientTape() as tape:\n",
    "                x_enc = encoder(X_batch, training=True)\n",
    "                y_recv = tf.grad_pass_through(channel)(x_enc) #forward pass:channel; backward pass Identity\n",
    "                x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "                y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "                score = NN_estimation(x,y)\n",
    "                loss = -MINE(score)\n",
    "                gradients = tape.gradient(loss, encoder.trainable_variables) \n",
    "                optimizer_ae.apply_gradients(zip(gradients, encoder.trainable_variables))\n",
    "            mi_avg = -mean_loss(loss)\n",
    "        with tf.GradientTape() as tape:\n",
    "            X_batch  = random_sample(batch_size) \n",
    "            x_enc = encoder(X_batch, training=True)\n",
    "            y_recv = channel(x_enc)\n",
    "            x = tf.reshape(x_enc, shape=[batch_size,2*n])\n",
    "            y = tf.reshape(y_recv, shape=[batch_size,2*n])\n",
    "            score = NN_estimation(x,y)\n",
    "            loss = -MINE(score)\n",
    "            gradients = tape.gradient(loss, NN_estimation.trainable_variables) \n",
    "            optimizer_mi.apply_gradients(zip(gradients, NN_estimation.trainable_variables))\n",
    "        print('Epoch: {}, Mi is {}'.format(epoch, mi_avg))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "def Test_AE():\n",
    "    '''Calculate Bit Error for varying SNRs'''\n",
    "    snr_range = np.linspace(0, 15, 31)\n",
    "    bber_vec = [None] * len(snr_range)\n",
    "        \n",
    "    for db in range(len(snr_range)):\n",
    "        for it in range(1,1000):\n",
    "            noise_std = EbNo_to_noise(snr_range[db])\n",
    "            X_batch  = random_sample(500)\n",
    "            code_word = encoder(X_batch)\n",
    "            if rayleigh:\n",
    "                rcvd_word = sample_Rayleigh_channel(code_word, noise_std)\n",
    "            else:\n",
    "                rcvd_word = code_word + tf.random.normal(tf.shape(code_word), mean=0.0, stddev=noise_std)\n",
    "            dcoded_msg = decoder(rcvd_word)\n",
    "            bber = B_Ber_m(X_batch, dcoded_msg)\n",
    "            bber_avg = mean_loss(bber)\n",
    "        bber_vec[db] = bber_avg\n",
    "        mean_loss.reset_states()\n",
    "        if (db % 6 == 0) & (db > 0):\n",
    "            print(f'Progress: {db} of {30} parts')\n",
    "\n",
    "    return (snr_range, bber_vec)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training in Epoch 1/1\n",
      "Epoch: 1, Mi is 2.23786997795105\n",
      "Training Bob in Epoch 1/5\n",
      "Epoch: 1, Mi is 2.5122218132019043\n",
      "Training Bob in Epoch 2/5\n",
      "Epoch: 2, Mi is 2.528424024581909\n",
      "Training Bob in Epoch 3/5\n",
      "Epoch: 3, Mi is 2.5307672023773193\n",
      "Training Bob in Epoch 4/5\n",
      "Epoch: 4, Mi is 2.5346245765686035\n",
      "Training Bob in Epoch 5/5\n",
      "Epoch: 5, Mi is 2.5440638065338135\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training Bob in Epoch 1/5\n",
      "Iteration: 10, Epoch: 1, Loss: -2.51781, Batch_BER: 0.86000\n",
      "Iteration: 20, Epoch: 1, Loss: -2.49270, Batch_BER: 0.68000\n",
      "Iteration: 30, Epoch: 1, Loss: -2.46878, Batch_BER: 0.65600\n",
      "Iteration: 40, Epoch: 1, Loss: -2.44603, Batch_BER: 0.64200\n",
      "Iteration: 50, Epoch: 1, Loss: -2.42439, Batch_BER: 0.51000\n",
      "Iteration: 60, Epoch: 1, Loss: -2.40376, Batch_BER: 0.45200\n",
      "Iteration: 70, Epoch: 1, Loss: -2.38401, Batch_BER: 0.39400\n",
      "Iteration: 80, Epoch: 1, Loss: -2.36508, Batch_BER: 0.37200\n",
      "Iteration: 90, Epoch: 1, Loss: -2.34683, Batch_BER: 0.29400\n",
      "Iteration: 100, Epoch: 1, Loss: -2.32922, Batch_BER: 0.31600\n",
      "Iteration: 110, Epoch: 1, Loss: -2.31226, Batch_BER: 0.30000\n",
      "Iteration: 120, Epoch: 1, Loss: -2.29583, Batch_BER: 0.22200\n",
      "Iteration: 130, Epoch: 1, Loss: -2.27987, Batch_BER: 0.18200\n",
      "Iteration: 140, Epoch: 1, Loss: -2.26442, Batch_BER: 0.14800\n",
      "Iteration: 150, Epoch: 1, Loss: -2.24941, Batch_BER: 0.11400\n",
      "Iteration: 160, Epoch: 1, Loss: -2.23484, Batch_BER: 0.11800\n",
      "Iteration: 170, Epoch: 1, Loss: -2.22066, Batch_BER: 0.09600\n",
      "Iteration: 180, Epoch: 1, Loss: -2.20686, Batch_BER: 0.06200\n",
      "Iteration: 190, Epoch: 1, Loss: -2.19343, Batch_BER: 0.07800\n",
      "Iteration: 200, Epoch: 1, Loss: -2.18032, Batch_BER: 0.07000\n",
      "Iteration: 210, Epoch: 1, Loss: -2.16752, Batch_BER: 0.07200\n",
      "Iteration: 220, Epoch: 1, Loss: -2.15498, Batch_BER: 0.06600\n",
      "Iteration: 230, Epoch: 1, Loss: -2.14271, Batch_BER: 0.06800\n",
      "Iteration: 240, Epoch: 1, Loss: -2.13070, Batch_BER: 0.05800\n",
      "Iteration: 250, Epoch: 1, Loss: -2.11889, Batch_BER: 0.05800\n",
      "Iteration: 260, Epoch: 1, Loss: -2.10731, Batch_BER: 0.06600\n",
      "Iteration: 270, Epoch: 1, Loss: -2.09594, Batch_BER: 0.06200\n",
      "Iteration: 280, Epoch: 1, Loss: -2.08476, Batch_BER: 0.05600\n",
      "Iteration: 290, Epoch: 1, Loss: -2.07378, Batch_BER: 0.05000\n",
      "Iteration: 300, Epoch: 1, Loss: -2.06297, Batch_BER: 0.06800\n",
      "Iteration: 310, Epoch: 1, Loss: -2.05231, Batch_BER: 0.05600\n",
      "Iteration: 320, Epoch: 1, Loss: -2.04179, Batch_BER: 0.05600\n",
      "Iteration: 330, Epoch: 1, Loss: -2.03144, Batch_BER: 0.06400\n",
      "Iteration: 340, Epoch: 1, Loss: -2.02124, Batch_BER: 0.04800\n",
      "Iteration: 350, Epoch: 1, Loss: -2.01115, Batch_BER: 0.06600\n",
      "Iteration: 360, Epoch: 1, Loss: -2.00123, Batch_BER: 0.04000\n",
      "Iteration: 370, Epoch: 1, Loss: -1.99142, Batch_BER: 0.06600\n",
      "Iteration: 380, Epoch: 1, Loss: -1.98171, Batch_BER: 0.05800\n",
      "Iteration: 390, Epoch: 1, Loss: -1.97214, Batch_BER: 0.06800\n",
      "Iteration: 400, Epoch: 1, Loss: -1.96266, Batch_BER: 0.06200\n",
      "Interim result for Epoch: 1, Loss: -1.96266, Batch_BER: 0.06200\n",
      "Training Bob in Epoch 2/5\n",
      "Iteration: 10, Epoch: 2, Loss: 0.29581, Batch_BER: 0.06400\n",
      "Iteration: 20, Epoch: 2, Loss: 0.29139, Batch_BER: 0.06800\n",
      "Iteration: 30, Epoch: 2, Loss: 0.28561, Batch_BER: 0.05600\n",
      "Iteration: 40, Epoch: 2, Loss: 0.27963, Batch_BER: 0.03600\n",
      "Iteration: 50, Epoch: 2, Loss: 0.27760, Batch_BER: 0.08200\n",
      "Iteration: 60, Epoch: 2, Loss: 0.27605, Batch_BER: 0.05200\n",
      "Iteration: 70, Epoch: 2, Loss: 0.27308, Batch_BER: 0.05200\n",
      "Iteration: 80, Epoch: 2, Loss: 0.27146, Batch_BER: 0.06000\n",
      "Iteration: 90, Epoch: 2, Loss: 0.26876, Batch_BER: 0.06800\n",
      "Iteration: 100, Epoch: 2, Loss: 0.26669, Batch_BER: 0.07600\n",
      "Iteration: 110, Epoch: 2, Loss: 0.26403, Batch_BER: 0.06200\n",
      "Iteration: 120, Epoch: 2, Loss: 0.26215, Batch_BER: 0.08200\n",
      "Iteration: 130, Epoch: 2, Loss: 0.26023, Batch_BER: 0.07400\n",
      "Iteration: 140, Epoch: 2, Loss: 0.25773, Batch_BER: 0.06600\n",
      "Iteration: 150, Epoch: 2, Loss: 0.25607, Batch_BER: 0.07200\n",
      "Iteration: 160, Epoch: 2, Loss: 0.25430, Batch_BER: 0.06800\n",
      "Iteration: 170, Epoch: 2, Loss: 0.25224, Batch_BER: 0.05600\n",
      "Iteration: 180, Epoch: 2, Loss: 0.24994, Batch_BER: 0.05200\n",
      "Iteration: 190, Epoch: 2, Loss: 0.24835, Batch_BER: 0.05200\n",
      "Iteration: 200, Epoch: 2, Loss: 0.24707, Batch_BER: 0.06600\n",
      "Iteration: 210, Epoch: 2, Loss: 0.24565, Batch_BER: 0.05200\n",
      "Iteration: 220, Epoch: 2, Loss: 0.24390, Batch_BER: 0.05800\n",
      "Iteration: 230, Epoch: 2, Loss: 0.24195, Batch_BER: 0.05800\n",
      "Iteration: 240, Epoch: 2, Loss: 0.24041, Batch_BER: 0.07000\n",
      "Iteration: 250, Epoch: 2, Loss: 0.23905, Batch_BER: 0.06800\n",
      "Iteration: 260, Epoch: 2, Loss: 0.23773, Batch_BER: 0.06000\n",
      "Iteration: 270, Epoch: 2, Loss: 0.23648, Batch_BER: 0.06800\n",
      "Iteration: 280, Epoch: 2, Loss: 0.23560, Batch_BER: 0.06200\n",
      "Iteration: 290, Epoch: 2, Loss: 0.23459, Batch_BER: 0.07800\n",
      "Iteration: 300, Epoch: 2, Loss: 0.23326, Batch_BER: 0.04800\n",
      "Iteration: 310, Epoch: 2, Loss: 0.23211, Batch_BER: 0.06600\n",
      "Iteration: 320, Epoch: 2, Loss: 0.23057, Batch_BER: 0.05000\n",
      "Iteration: 330, Epoch: 2, Loss: 0.22922, Batch_BER: 0.05200\n",
      "Iteration: 340, Epoch: 2, Loss: 0.22804, Batch_BER: 0.04400\n",
      "Iteration: 350, Epoch: 2, Loss: 0.22726, Batch_BER: 0.06400\n",
      "Iteration: 360, Epoch: 2, Loss: 0.22613, Batch_BER: 0.05600\n",
      "Iteration: 370, Epoch: 2, Loss: 0.22521, Batch_BER: 0.04400\n",
      "Iteration: 380, Epoch: 2, Loss: 0.22456, Batch_BER: 0.05600\n",
      "Iteration: 390, Epoch: 2, Loss: 0.22371, Batch_BER: 0.05200\n",
      "Iteration: 400, Epoch: 2, Loss: 0.22263, Batch_BER: 0.04800\n",
      "Interim result for Epoch: 2, Loss: 0.22263, Batch_BER: 0.04800\n",
      "Training Bob in Epoch 3/5\n",
      "Iteration: 10, Epoch: 3, Loss: 0.19627, Batch_BER: 0.05200\n",
      "Iteration: 20, Epoch: 3, Loss: 0.19146, Batch_BER: 0.06400\n",
      "Iteration: 30, Epoch: 3, Loss: 0.18873, Batch_BER: 0.05600\n",
      "Iteration: 40, Epoch: 3, Loss: 0.18879, Batch_BER: 0.05000\n",
      "Iteration: 50, Epoch: 3, Loss: 0.18887, Batch_BER: 0.08400\n",
      "Iteration: 60, Epoch: 3, Loss: 0.18861, Batch_BER: 0.06200\n",
      "Iteration: 70, Epoch: 3, Loss: 0.18765, Batch_BER: 0.05000\n",
      "Iteration: 80, Epoch: 3, Loss: 0.18738, Batch_BER: 0.07000\n",
      "Iteration: 90, Epoch: 3, Loss: 0.18684, Batch_BER: 0.05200\n",
      "Iteration: 100, Epoch: 3, Loss: 0.18581, Batch_BER: 0.05400\n",
      "Iteration: 110, Epoch: 3, Loss: 0.18565, Batch_BER: 0.05800\n",
      "Iteration: 120, Epoch: 3, Loss: 0.18503, Batch_BER: 0.05800\n",
      "Iteration: 130, Epoch: 3, Loss: 0.18400, Batch_BER: 0.06600\n",
      "Iteration: 140, Epoch: 3, Loss: 0.18398, Batch_BER: 0.08400\n",
      "Iteration: 150, Epoch: 3, Loss: 0.18407, Batch_BER: 0.06200\n",
      "Iteration: 160, Epoch: 3, Loss: 0.18341, Batch_BER: 0.06200\n",
      "Iteration: 170, Epoch: 3, Loss: 0.18283, Batch_BER: 0.06200\n",
      "Iteration: 180, Epoch: 3, Loss: 0.18289, Batch_BER: 0.06200\n",
      "Iteration: 190, Epoch: 3, Loss: 0.18238, Batch_BER: 0.08000\n",
      "Iteration: 200, Epoch: 3, Loss: 0.18206, Batch_BER: 0.08000\n",
      "Iteration: 210, Epoch: 3, Loss: 0.18185, Batch_BER: 0.04400\n",
      "Iteration: 220, Epoch: 3, Loss: 0.18146, Batch_BER: 0.06000\n",
      "Iteration: 230, Epoch: 3, Loss: 0.18149, Batch_BER: 0.04600\n",
      "Iteration: 240, Epoch: 3, Loss: 0.18121, Batch_BER: 0.05800\n",
      "Iteration: 250, Epoch: 3, Loss: 0.18086, Batch_BER: 0.05200\n",
      "Iteration: 260, Epoch: 3, Loss: 0.18056, Batch_BER: 0.05200\n",
      "Iteration: 270, Epoch: 3, Loss: 0.18029, Batch_BER: 0.06800\n",
      "Iteration: 280, Epoch: 3, Loss: 0.18013, Batch_BER: 0.07000\n",
      "Iteration: 290, Epoch: 3, Loss: 0.18043, Batch_BER: 0.05800\n",
      "Iteration: 300, Epoch: 3, Loss: 0.17983, Batch_BER: 0.05800\n",
      "Iteration: 310, Epoch: 3, Loss: 0.17962, Batch_BER: 0.08600\n",
      "Iteration: 320, Epoch: 3, Loss: 0.17951, Batch_BER: 0.05800\n",
      "Iteration: 330, Epoch: 3, Loss: 0.17975, Batch_BER: 0.07400\n",
      "Iteration: 340, Epoch: 3, Loss: 0.17922, Batch_BER: 0.04800\n",
      "Iteration: 350, Epoch: 3, Loss: 0.17913, Batch_BER: 0.06400\n",
      "Iteration: 360, Epoch: 3, Loss: 0.17897, Batch_BER: 0.05200\n",
      "Iteration: 370, Epoch: 3, Loss: 0.17881, Batch_BER: 0.05800\n",
      "Iteration: 380, Epoch: 3, Loss: 0.17864, Batch_BER: 0.07400\n",
      "Iteration: 390, Epoch: 3, Loss: 0.17850, Batch_BER: 0.06600\n",
      "Iteration: 400, Epoch: 3, Loss: 0.17812, Batch_BER: 0.05800\n",
      "Interim result for Epoch: 3, Loss: 0.17812, Batch_BER: 0.05800\n",
      "Training Bob in Epoch 4/5\n",
      "Iteration: 10, Epoch: 4, Loss: 0.17698, Batch_BER: 0.07200\n",
      "Iteration: 20, Epoch: 4, Loss: 0.17303, Batch_BER: 0.05200\n",
      "Iteration: 30, Epoch: 4, Loss: 0.16980, Batch_BER: 0.05800\n",
      "Iteration: 40, Epoch: 4, Loss: 0.17060, Batch_BER: 0.06800\n",
      "Iteration: 50, Epoch: 4, Loss: 0.16861, Batch_BER: 0.05200\n",
      "Iteration: 60, Epoch: 4, Loss: 0.16796, Batch_BER: 0.05200\n",
      "Iteration: 70, Epoch: 4, Loss: 0.16613, Batch_BER: 0.04600\n",
      "Iteration: 80, Epoch: 4, Loss: 0.16595, Batch_BER: 0.05400\n",
      "Iteration: 90, Epoch: 4, Loss: 0.16652, Batch_BER: 0.05600\n",
      "Iteration: 100, Epoch: 4, Loss: 0.16847, Batch_BER: 0.06800\n",
      "Iteration: 110, Epoch: 4, Loss: 0.16918, Batch_BER: 0.06600\n",
      "Iteration: 120, Epoch: 4, Loss: 0.17002, Batch_BER: 0.04400\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration: 130, Epoch: 4, Loss: 0.16943, Batch_BER: 0.06800\n",
      "Iteration: 140, Epoch: 4, Loss: 0.16965, Batch_BER: 0.05000\n",
      "Iteration: 150, Epoch: 4, Loss: 0.17004, Batch_BER: 0.07000\n",
      "Iteration: 160, Epoch: 4, Loss: 0.17003, Batch_BER: 0.05200\n",
      "Iteration: 170, Epoch: 4, Loss: 0.16961, Batch_BER: 0.06200\n",
      "Iteration: 180, Epoch: 4, Loss: 0.16986, Batch_BER: 0.06800\n",
      "Iteration: 190, Epoch: 4, Loss: 0.16966, Batch_BER: 0.07000\n",
      "Iteration: 200, Epoch: 4, Loss: 0.16929, Batch_BER: 0.06400\n",
      "Iteration: 210, Epoch: 4, Loss: 0.16961, Batch_BER: 0.05800\n",
      "Iteration: 220, Epoch: 4, Loss: 0.16991, Batch_BER: 0.06600\n",
      "Iteration: 230, Epoch: 4, Loss: 0.16993, Batch_BER: 0.06000\n",
      "Iteration: 240, Epoch: 4, Loss: 0.16971, Batch_BER: 0.07000\n",
      "Iteration: 250, Epoch: 4, Loss: 0.16984, Batch_BER: 0.08000\n",
      "Iteration: 260, Epoch: 4, Loss: 0.16989, Batch_BER: 0.05400\n",
      "Iteration: 270, Epoch: 4, Loss: 0.17021, Batch_BER: 0.04200\n",
      "Iteration: 280, Epoch: 4, Loss: 0.17030, Batch_BER: 0.04800\n",
      "Iteration: 290, Epoch: 4, Loss: 0.17034, Batch_BER: 0.03200\n",
      "Iteration: 300, Epoch: 4, Loss: 0.17020, Batch_BER: 0.06800\n",
      "Iteration: 310, Epoch: 4, Loss: 0.16983, Batch_BER: 0.04200\n",
      "Iteration: 320, Epoch: 4, Loss: 0.17008, Batch_BER: 0.06400\n",
      "Iteration: 330, Epoch: 4, Loss: 0.16986, Batch_BER: 0.06800\n",
      "Iteration: 340, Epoch: 4, Loss: 0.16965, Batch_BER: 0.04400\n",
      "Iteration: 350, Epoch: 4, Loss: 0.16904, Batch_BER: 0.04000\n",
      "Iteration: 360, Epoch: 4, Loss: 0.16894, Batch_BER: 0.05400\n",
      "Iteration: 370, Epoch: 4, Loss: 0.16887, Batch_BER: 0.04000\n",
      "Iteration: 380, Epoch: 4, Loss: 0.16902, Batch_BER: 0.06200\n",
      "Iteration: 390, Epoch: 4, Loss: 0.16901, Batch_BER: 0.05000\n",
      "Iteration: 400, Epoch: 4, Loss: 0.16906, Batch_BER: 0.05000\n",
      "Interim result for Epoch: 4, Loss: 0.16906, Batch_BER: 0.05000\n",
      "Training Bob in Epoch 5/5\n",
      "Iteration: 10, Epoch: 5, Loss: 0.16969, Batch_BER: 0.04600\n",
      "Iteration: 20, Epoch: 5, Loss: 0.16205, Batch_BER: 0.06600\n",
      "Iteration: 30, Epoch: 5, Loss: 0.16533, Batch_BER: 0.06800\n",
      "Iteration: 40, Epoch: 5, Loss: 0.16575, Batch_BER: 0.06400\n",
      "Iteration: 50, Epoch: 5, Loss: 0.16531, Batch_BER: 0.05400\n",
      "Iteration: 60, Epoch: 5, Loss: 0.16434, Batch_BER: 0.07000\n",
      "Iteration: 70, Epoch: 5, Loss: 0.16441, Batch_BER: 0.06000\n",
      "Iteration: 80, Epoch: 5, Loss: 0.16337, Batch_BER: 0.04400\n",
      "Iteration: 90, Epoch: 5, Loss: 0.16321, Batch_BER: 0.07800\n",
      "Iteration: 100, Epoch: 5, Loss: 0.16312, Batch_BER: 0.06800\n",
      "Iteration: 110, Epoch: 5, Loss: 0.16315, Batch_BER: 0.06200\n",
      "Iteration: 120, Epoch: 5, Loss: 0.16401, Batch_BER: 0.07000\n",
      "Iteration: 130, Epoch: 5, Loss: 0.16410, Batch_BER: 0.05000\n",
      "Iteration: 140, Epoch: 5, Loss: 0.16513, Batch_BER: 0.06400\n",
      "Iteration: 150, Epoch: 5, Loss: 0.16516, Batch_BER: 0.06400\n",
      "Iteration: 160, Epoch: 5, Loss: 0.16500, Batch_BER: 0.06200\n",
      "Iteration: 170, Epoch: 5, Loss: 0.16506, Batch_BER: 0.06800\n",
      "Iteration: 180, Epoch: 5, Loss: 0.16582, Batch_BER: 0.05400\n",
      "Iteration: 190, Epoch: 5, Loss: 0.16636, Batch_BER: 0.05600\n",
      "Iteration: 200, Epoch: 5, Loss: 0.16679, Batch_BER: 0.05800\n",
      "Iteration: 210, Epoch: 5, Loss: 0.16706, Batch_BER: 0.05200\n",
      "Iteration: 220, Epoch: 5, Loss: 0.16687, Batch_BER: 0.07600\n",
      "Iteration: 230, Epoch: 5, Loss: 0.16706, Batch_BER: 0.06000\n",
      "Iteration: 240, Epoch: 5, Loss: 0.16671, Batch_BER: 0.05800\n",
      "Iteration: 250, Epoch: 5, Loss: 0.16698, Batch_BER: 0.07200\n",
      "Iteration: 260, Epoch: 5, Loss: 0.16711, Batch_BER: 0.06200\n",
      "Iteration: 270, Epoch: 5, Loss: 0.16694, Batch_BER: 0.06800\n",
      "Iteration: 280, Epoch: 5, Loss: 0.16693, Batch_BER: 0.07200\n",
      "Iteration: 290, Epoch: 5, Loss: 0.16628, Batch_BER: 0.05000\n",
      "Iteration: 300, Epoch: 5, Loss: 0.16567, Batch_BER: 0.05000\n",
      "Iteration: 310, Epoch: 5, Loss: 0.16565, Batch_BER: 0.04200\n",
      "Iteration: 320, Epoch: 5, Loss: 0.16536, Batch_BER: 0.04200\n",
      "Iteration: 330, Epoch: 5, Loss: 0.16516, Batch_BER: 0.06600\n",
      "Iteration: 340, Epoch: 5, Loss: 0.16554, Batch_BER: 0.06000\n",
      "Iteration: 350, Epoch: 5, Loss: 0.16516, Batch_BER: 0.05400\n",
      "Iteration: 360, Epoch: 5, Loss: 0.16502, Batch_BER: 0.04400\n",
      "Iteration: 370, Epoch: 5, Loss: 0.16514, Batch_BER: 0.06000\n",
      "Iteration: 380, Epoch: 5, Loss: 0.16514, Batch_BER: 0.07200\n",
      "Iteration: 390, Epoch: 5, Loss: 0.16503, Batch_BER: 0.05800\n",
      "Iteration: 400, Epoch: 5, Loss: 0.16486, Batch_BER: 0.04600\n",
      "Interim result for Epoch: 5, Loss: 0.16486, Batch_BER: 0.04600\n"
     ]
    }
   ],
   "source": [
    "score_fn = NN_function(**critic_params)\n",
    "train_mi(score_fn, n_epochs=1, n_steps=500, batch_size=64)\n",
    "train_encoder(score_fn, n_epochs=5, n_steps=400, batch_size=64, learning_rate=0.005)\n",
    "test_encoding(M, 1)\n",
    "train_decoder(n_epochs=5, n_steps=400, batch_size=500, learning_rate=0.005, plot_encoding=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Progress: 6 of 30 parts\n",
      "Progress: 12 of 30 parts\n",
      "Progress: 18 of 30 parts\n",
      "Progress: 24 of 30 parts\n",
      "Progress: 30 of 30 parts\n"
     ]
    }
   ],
   "source": [
    "bber_data = Test_AE()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 576x360 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Approximate 16 QAM Error\n",
    "def SIXT_QAM_sim(ebno):\n",
    "    return (3.0/2)*special.erfc(np.sqrt((4.0/10)*10.**(ebno/10)))\n",
    "\n",
    "def MQAM_rayleigh_approx(M, ebnodb):\n",
    "    ebno = 10.**(ebnodb/10)\n",
    "    esno = 4*ebno\n",
    "    #Goldsmith, p.185, 6.3.2, Eqn 6.61, alphaM=4, betaM=3/(M-1)\n",
    "    a=3.25 #adjusted mean number of neighbors\n",
    "    b=3/(M-1)\n",
    "    e=b*esno\n",
    "    return (a/2)*(1-np.sqrt(0.5*e / (1+0.5*e) ) ), a/(2*b*esno)\n",
    "\n",
    "ebnodbs = np.linspace(0,15,16)\n",
    "fig = plt.figure(figsize=(8, 5))\n",
    "plt.semilogy(bber_data[0], bber_data[1], 'o-')\n",
    "\n",
    "if rayleigh:\n",
    "    a, b = MQAM_rayleigh_approx(16,ebnodbs)\n",
    "    plt.plot(ebnodbs, a, 's-')\n",
    "else:\n",
    "    plt.plot(ebnodbs, SIXT_QAM_sim(ebnodbs), 's-')\n",
    "    \n",
    "plt.gca().set_ylim(1e-5, 1)\n",
    "plt.gca().set_xlim(0, 15)\n",
    "plt.ylabel(\"Batch Symbol Error Rate\", fontsize=14, rotation=90)\n",
    "plt.xlabel(\"EbN0 [dB]\", fontsize=18)\n",
    "plt.legend(['AE with MINE', '16QAM'],\n",
    "           prop={'size': 14}, loc='upper right');\n",
    "plt.grid(True, which=\"both\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
